package site.edody.dframe.websocket;

import org.java_websocket.WebSocket;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;

/**
 * DFrame WebSocket 服务器
 * @author Dody
 */
public class DFrameWebSocketServer extends WebSocketServer {

    private static final Logger log = LoggerFactory.getLogger(DFrameWebSocketServer.class);

    private final String serverName;
    private final DFrameWebSocketListener messageListener;
    private final DFrameWebSocketSessionCache sessionCache;
    private final DFrameWebSocketDataCache dataCache;

    public DFrameWebSocketServer(String serverName, int port,
                                 DFrameWebSocketListener messageListener,
                                 DFrameWebSocketSessionCache sessionCache,
                                 DFrameWebSocketDataCache dataCache) {
        super(new InetSocketAddress(port));
        this.serverName = serverName;
        this.messageListener = messageListener;
        this.sessionCache = sessionCache;
        this.dataCache = dataCache;
    }

    @Override
    public void onOpen(WebSocket conn, ClientHandshake handshake) {
        String clientId = generateClientId(conn);
        sessionCache.addSession(serverName, clientId, conn);

        log.info("[DFrame-{}] Client connected: {} - {}", serverName, clientId,
                conn.getRemoteSocketAddress().getAddress().getHostAddress());

        if (messageListener != null) {
            try {
                messageListener.onOpen(serverName, conn, handshake);
            } catch (Exception ex) {
                log.error("[DFrame-{}] Open listener exception", serverName, ex);
            }
        }
    }

    @Override
    public void onClose(WebSocket conn, int code, String reason, boolean remote) {
        String clientId = findClientId(conn);
        if (clientId != null) {
            sessionCache.removeSession(serverName, clientId);
            // 清理该客户端相关的数据缓存
            dataCache.clearSessionAudioData(serverName, clientId);
        }

        log.info("[DFrame-{}] Client disconnected: {} - {}", serverName, clientId, reason);

        if (messageListener != null) {
            try {
                messageListener.onClose(serverName, conn, code, reason, remote);
            } catch (Exception ex) {
                log.error("[DFrame-{}] Close listener exception", serverName, ex);
            }
        }
    }

    @Override
    public void onMessage(WebSocket conn, String message) {
        String clientId = findClientId(conn);
        log.debug("[DFrame-{}] Received message from {}: {}", serverName, clientId, message);

        if (messageListener != null) {
            try {
                messageListener.onMessage(serverName, conn, message);
            } catch (Exception ex) {
                log.error("[DFrame-{}] Message listener exception", serverName, ex);
            }
        }
    }

    @Override
    public void onError(WebSocket conn, Exception ex) {
        String clientId = findClientId(conn);
        log.error("[DFrame-{}] WebSocket error for client {}: {}", serverName, clientId, ex.getMessage());

        if (messageListener != null) {
            try {
                messageListener.onError(serverName, conn, ex);
            } catch (Exception e) {
                log.error("[DFrame-{}] Error listener exception", serverName, e);
            }
        }
    }

    @Override
    public void onStart() {
        log.info("[DFrame-{}] WebSocket server started on port: {}", serverName, getPort());
        setConnectionLostTimeout(100);
    }

    private String generateClientId(WebSocket conn) {
        return conn.getRemoteSocketAddress().toString() + "-" + System.currentTimeMillis();
    }

    private String findClientId(WebSocket conn) {
        return sessionCache.findSessionId(serverName, conn);
    }

    public String getServerName() {
        return serverName;
    }

    public DFrameWebSocketDataCache getDataCache() {
        return dataCache;
    }

    public DFrameWebSocketSessionCache getSessionCache() {
        return sessionCache;
    }
}